/**
 * Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.huwei.modules.base.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.huwei.modules.base.annotations.BussinessLog;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * 业务执行log记录
 */
@Slf4j
@Aspect
@Component
public class BussinessLogAop {
    @Pointcut(value = "@annotation(com.huwei.modules.base.annotations.BussinessLog)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
    	Object result = null;
    	try {
	        //先执行业务
	        result = point.proceed();
    	} catch (Exception e) {
    		//业务执行失败
    		bussinessErrorLog(point ,e);
    		throw e;
		}

        try {
        	bussinessLog(point ,result);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }

        return result;
    }

    private void bussinessErrorLog(ProceedingJoinPoint point ,Exception e) throws Exception {
		bussinessLog(point, null);
		log.error("---> execution bussiness error ,cause: " ,e);
	}

	private void bussinessLog(ProceedingJoinPoint point ,Object result) throws Exception {
        LogInfo logInfo = parse(point, result);
        log.info("---> execution bussiness : " + "key="+ logInfo.getBussinessKey() + " value=" + logInfo.getBussinessValue());
        log.info("---> user info : " + logInfo.getUserUuid());
		log.info("---> class name : " + logInfo.getClassName());
		log.info("---> method name : " + logInfo.getMethodName());
		log.info("---> paramters name : " + logInfo.getParams());
		log.info("---> bussiness result : " + logInfo.getResult());
    }
	
	private LogInfo parse(ProceedingJoinPoint point ,Object result) throws Exception {
    	
        //获取拦截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();

        //获取拦截方法的参数
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();
        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append("&");
        }

        //获取操作名称
        BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
        String bussinessValue = annotation.value();
        String bussinessKey = annotation.key();
        boolean recordResult = annotation.recordResult();
        return new LogInfo("", className, methodName, sb.toString().substring(0, sb.length()-1), bussinessKey, bussinessValue, recordResult,result);
	}
	
	@Data
	private class LogInfo{
		String userUuid;
		String className;
		String methodName;
		String params;
		String bussinessKey;
		String bussinessValue;
		boolean recordResult;
		Object result;
		public LogInfo(String userUuid, String className, String methodName, String params,
				String bussinessKey, String bussinessValue, boolean recordResult, Object result) {
			this.userUuid = userUuid;
			this.className = className;
			this.methodName = methodName;
			this.params = params;
			this.bussinessKey = bussinessKey;
			this.bussinessValue = bussinessValue;
			this.recordResult = recordResult;
			this.result = result;
		}
	} 
}